#ifndef __THREAD_POOL_HPP__
#define __THREAD_POOL_HPP__

#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <atomic>
#include <functional>
#include <future>
#include <memory>
#include <ctime>
#include <cstring>

#define DBG 0
#define INF 1
#define ERR 2
#define FAT 3

#define DEFAULT_LEVEL DBG

#define LOG(level, format, ...)                                                    \
    {                                                                              \
        if (level < DEFAULT_LEVEL)                                                 \
            return;                                                                \
        time_t tm = time(nullptr);                                                 \
        struct tm *stm = localtime(&tm);                                           \
        char tmp_time[32];                                                         \
        bzero(tmp_time, sizeof(tmp_time));                                         \
        strftime(tmp_time, sizeof(tmp_time) - 1, "%m-%d %T", stm);                 \
        fprintf(stdout, "[%s:%d]" format "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
    }

#define DLOG(format, ...) LOG(DBG, format, ##__VA_ARGS__)
#define iLOG(format, ...) LOG(INF, format, ##__VA_ARGS__)
#define ELOG(format, ...) LOG(ERR, format, ##__VA_ARGS__)
#define FLOG(format, ...) LOG(FAT, format, ##__VA_ARGS__)

using Callback = std::function<void()>;
class ThreadPool
{
public:
    static ThreadPool &getInstance()
    {
        static ThreadPool tp;
        return tp;
    }

    /*启动线程*/
    void start()
    {
        _manager_thread = std::move(std::thread(std::bind(&ThreadPool::managerEntry, this)));
        for (int i = 0; i < _cur_thread_num; i++)
        {
            std::thread t(std::bind(&ThreadPool::workEntry, this));
            _works_threads.insert(std::make_pair(t.get_id(), std::move(t)));
        }
    }

    /*将任务回调函数添加进任务队列,返回得到future对象*/
    template <typename F, typename... Args>
    auto addTask(F &&f, Args &&...args) -> std::future<typename std::result_of<F(Args...)>::type>
    {
        // 1.构成packaged_task可调用对象
        using resutl_type = typename std::result_of<F(Args...)>::type;
        auto cb = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
        //packaged_task对象禁止拷贝,使用shared_ptr智能指针管理packaged_task,防止局部变量出了作用域就销毁了
        auto pack = std::make_shared<std::packaged_task<resutl_type()>>(cb);
        auto future_result = pack->get_future();
        // 2.压入任务队列
        {
            std::unique_lock<std::mutex> lock(_task_queue_mutex);
            _tasks.emplace([pack]()
                           { (*pack)(); });
            _task_queue_cond.notify_one();
        }
        // 3.返回future对象
        return std::move(future_result);
    }

    ~ThreadPool()
    {
        _stop_flag = true;
        _task_queue_cond.notify_all();
        stop();
    }

private:
    // std::thread::hardware_concurrency()
    ThreadPool(int min_num = 2, int max_num = 6)
        : _min_thread_num(min_num),
          _max_thread_num(max_num),
          _cur_thread_num(min_num),
          _free_thread_num(min_num),
          _exit_thread_num(0),
          _stop_flag(false)
    {
    }

    /*管理线程入口函数*/
    void managerEntry()
    {
        while (!_stop_flag)
        {
            // 管理线程每隔指定秒数醒来管理工作线程
            std::this_thread::sleep_for(std::chrono::seconds(_manager_sleep_time));
            // 制定策略
            // 1.当前线程数大于最小线程数,空闲线程大于当前线程总线程数的一半,就删除两个线程
            // 2.空闲线程小于2个,同时当前总线程数小于等于最大线程数,则创建新线程

            if (_cur_thread_num > _min_thread_num && _free_thread_num > _cur_thread_num / 2)
            {
                // 删除线程
                _exit_thread_num.store(2);
                _task_queue_cond.notify_all();
                std::unique_lock<std::mutex> lock(_id_mutex);
                for (auto &id : _thread_ids)
                {
                    auto iter = _works_threads.find(id);
                    if (iter != _works_threads.end())
                    {
                        DLOG("管理线程回收了工作线程,线程id: %x", id);
                        iter->second.join();
                        _works_threads.erase(iter);
                    }
                }
                _thread_ids.clear();
            }
            else if (_free_thread_num < 2 && _cur_thread_num < _max_thread_num)
            {
                // 新添线程
                _cur_thread_num++;
                _free_thread_num++;
                std::thread t(std::bind(&ThreadPool::workEntry, this));
                DLOG("管理线程新增一个工作线程,线程id: %x", t.get_id());
                _works_threads.insert(std::make_pair(t.get_id(), std::move(t)));
            }
        }
    }

    /*工作线程入口函数*/
    void workEntry()
    {
        while (!_stop_flag)
        {
            Callback task = nullptr;
            {
                std::unique_lock<std::mutex> lock(_task_queue_mutex);
                while (!_stop_flag&&_tasks.empty())
                {
                    _task_queue_cond.wait(lock);
                    if (_exit_thread_num > 0)
                    {
                        _cur_thread_num--;
                        _free_thread_num--;
                        _exit_thread_num--;
                        DLOG("工作线程要退出啦,线程id: %x", std::this_thread::get_id());
                        std::unique_lock<std::mutex> lock(_id_mutex);
                        _thread_ids.emplace_back(std::this_thread::get_id());
                        return;
                    }
                }

                if (!_tasks.empty())
                {
                    DLOG("取出一个任务,工作线程id: %x", std::this_thread::get_id());
                    task = std::move(_tasks.front());
                    _tasks.pop();
                }
            }
            if (task)
            {
                _free_thread_num--;
                task();
                _free_thread_num++;
            }
        }
    }

    void stop()
    {
        DLOG("#########回收所有线程开始##########");

        if (_manager_thread.joinable())
        {
            DLOG("管理线程正常退出,线程id: %x", _manager_thread.get_id());
            _manager_thread.join();
        }

        for (auto &iter : _works_threads)
        {
            if (iter.second.joinable())
            {
                DLOG("工作线程正常退出,线程id: %x", iter.first);
                iter.second.join();
            }
        }

        DLOG("#########回收所有线程结束##########");
    }

private:
    const size_t _manager_sleep_time = 3;

private:
    std::thread _manager_thread;                                     // 管理线程
    std::vector<std::thread::id> _thread_ids;                        // 记录退出线程id
    std::unordered_map<std::thread::id, std::thread> _works_threads; // 管理工作线程
    std::queue<Callback> _tasks;                                     // 任务队列
    std::atomic<int> _min_thread_num;                                // 记录最小线程数
    std::atomic<int> _max_thread_num;                                // 记录最大线程数;
    std::atomic<int> _cur_thread_num;                                // 记录当前线程数
    std::atomic<int> _free_thread_num;                               // 记录当前空闲线程数
    std::atomic<int> _exit_thread_num;                               // 记录当前工作线程数
    std::atomic<bool> _stop_flag;                                    // 停止标志
    std::mutex _task_queue_mutex;                                    // 任务队列互斥锁
    std::mutex _id_mutex;                                            // 线程id集合互斥锁
    std::condition_variable _task_queue_cond;                        // 任务队列条件变量
};

#endif